#include <basic/errcode.h>
#include <basic/mail.h>
#include <basic/ossl.h>

#define _TIMEOUT                120000
#define _DEF_SMTP_PORT          25
#define _SMTP_CODE_SIZE         3
#define _BOUNDARY_SIZE          32

static int _talk(SMTP smtp, int write_size)
{/*{{{*/
    int timeout=_TIMEOUT;
    char buf[_SMTP_CODE_SIZE+1];
    int res;

    if((res=skt_write(smtp->skt, (unsigned char *)smtp->_writebuf,
                    &write_size, SKT_W_SIZE, &timeout)) < 0)
    {
        if(res == RET_ERR_TIMEOUT || res == RET_ERR_ABORT)
            return(res);
        else
            return(-1);
    }
    if((res=skt_read(smtp->skt, (unsigned char *)smtp->_readbuf,
                    &write_size, SKT_R_RAW, &timeout)) < 0)
    {
        if(res == RET_ERR_TIMEOUT || res == RET_ERR_ABORT)
            return(res);
        else
            return(-1);
    }
    //smtp->_readbuf[write_size]=ZERO;
    //printf(smtp->_readbuf);
    strncpy(buf, smtp->_readbuf, _SMTP_CODE_SIZE);
    buf[_SMTP_CODE_SIZE]=ZERO;
    return(atoi(buf));
}/*}}}*/

int smtp_create(char *host_addr, SMTP *psmtp)
{/*{{{*/
    int res;
    SMTP smtp;
    char addr[FILE_NM_SIZE+1];
    int timeout;
    int rtn=0;
    int offset;
    char *readbuf, *writebuf;

    if(!psmtp)
        return(RET_ERR_PARA);
    *psmtp=(SMTP)NULL;
    if(!host_addr)
        return(RET_ERR_PARA);

    ALLOC_RETERR(smtp, struct _smtp, RET_ERR_ALLOC);
    if(host_addr_parse(host_addr, &smtp->addrinfo))
    {   free(smtp); return(RET_ERR_PARA);   }
    if(smtp->addrinfo.port != _DEF_SMTP_PORT)
    {
        if(smtp->addrinfo.port < 1024)
        {
            if(smtp->addrinfo.port == 0)
            {   smtp->addrinfo.port=_DEF_SMTP_PORT;   }
            else
            {   free(smtp); return(RET_ERR_PARA);   }
        }
    }
    smtp->skt=(SKT)NULL;
    ALLOC_MULTI_DOERR(smtp->_readbuf, char, KB(4),
            free(smtp); return(RET_ERR_ALLOC));
    ALLOC_ARRAY_DOERR(smtp->_writebuf, char, KB(4),
            smtp->_writebuf_size,
            free(smtp->_readbuf); free(smtp);
            return(RET_ERR_ALLOC));
    readbuf=smtp->_readbuf;
    writebuf=smtp->_writebuf;

    timeout=_TIMEOUT;
    sprintf(addr, "%s:%d", smtp->addrinfo.host, smtp->addrinfo.port);
    if(skt_connect(addr, 0, &timeout, &smtp->skt))  
    {   rtn=RET_ERR_CONNECT; goto GO_OUT;   }
    if((res=skt_read(smtp->skt, (unsigned char *)readbuf, &offset,
                    SKT_R_RAW, &timeout)) < 0)
    {
        if(res == RET_ERR_TIMEOUT || res == RET_ERR_ABORT)
            rtn=res;
        else
            rtn=-2;
        goto GO_OUT;
    }
    readbuf[_SMTP_CODE_SIZE]=ZERO;
    if(atoi(readbuf) != 220)
    {   rtn=-3; goto GO_OUT;   }
    smtp->auth=_NOAUTH_;
    if(gethostname(smtp->sender, sizeof(smtp->sender)))
        strcpy(smtp->sender, "mailsender");
    offset=sprintf(writebuf, "EHLO %s"CRLF, smtp->sender);
    if((res=_talk(smtp, offset)) < 0)
    {   rtn=res; goto GO_OUT;   }
    if(res == 250)
    {
        regex_t reg;
        regmatch_t pmatch[2];

        if(regcomp(&reg, "AUTH=([^\r]*)", REG_EXTENDED) == 0) 
        {
            if(regexec(&reg, readbuf, 2, pmatch, 0) == 0)
            {
                int len=pmatch[1].rm_eo - pmatch[1].rm_so;
                char auth[20];

                strncpy(auth, readbuf+pmatch[1].rm_so, len);
                auth[len]=ZERO;
                if(strstr(auth, "LOGIN"))
                {
                    smtp->auth=_LOGIN_;
                    if(strstr(auth, "PLAIN"))
                        smtp->auth=_PLAIN_;
                }
            }
            regfree(&reg);
        }
    }
    if(smtp->auth == _LOGIN_ || smtp->auth == _PLAIN_)
    {
        if(BUF_IS_CLR(smtp->addrinfo.user) ||
                BUF_IS_CLR(smtp->addrinfo.passwd))
        {   rtn=-4; goto GO_OUT;   }
    }
    if(smtp->auth == _LOGIN_)
    {
        offset=sprintf(writebuf, "AUTH LOGIN"CRLF);
        if((res=_talk(smtp, offset)) < 0)
        {   rtn=res; goto GO_OUT;   }
        if(res != 334)
        {   rtn=-5; goto GO_OUT;   }
        ossl_encode(OSSL_ENCODE_BASE64,
                (unsigned char *)smtp->addrinfo.user,
                strlen(smtp->addrinfo.user), readbuf, &offset);
        readbuf[offset-1]=ZERO;
        offset=sprintf(writebuf, "%s"CRLF, readbuf);
        if((res=_talk(smtp, offset)) < 0)
        {   rtn=res; goto GO_OUT;   }
        if(res != 334)
        {   rtn=-6; goto GO_OUT;   }
        ossl_encode(OSSL_ENCODE_BASE64,
                (unsigned char *)smtp->addrinfo.passwd,
                strlen(smtp->addrinfo.passwd), readbuf, &offset);
        readbuf[offset-1]=ZERO;
        offset=sprintf(writebuf, "%s"CRLF, readbuf);
        if((res=_talk(smtp, offset)) < 0)
        {   rtn=res; goto GO_OUT;   }
        if(res != 235)
        {   rtn=-7; goto GO_OUT;   }
    }
    else if(smtp->auth == _PLAIN_)
    {
        int size;
        char buf[100];

        strcpy(buf, smtp->addrinfo.user);
        size=strlen(smtp->addrinfo.user)+1;
        strcpy(buf+size, smtp->addrinfo.user);
        size+=strlen(smtp->addrinfo.user)+1;
        strcpy(buf+size, smtp->addrinfo.passwd);
        size+=strlen(smtp->addrinfo.passwd);
        ossl_encode(OSSL_ENCODE_BASE64, (unsigned char *)buf, size,
                readbuf, &offset);
        readbuf[offset-1]=ZERO;
        offset=sprintf(writebuf, "AUTH PLAIN %s"CRLF, readbuf);
        if((res=_talk(smtp, offset)) < 0)
        {   rtn=res; goto GO_OUT;   }
        if(res != 235)
        {   rtn=-8; goto GO_OUT;   }
    }
    else
    {
        offset=sprintf(writebuf, "HELO %s"CRLF, smtp->sender);
        if((res=_talk(smtp, offset)) < 0)
        {   rtn=res; goto GO_OUT;   }
        if(res != 250)
        {   rtn=-9; goto GO_OUT;   }
    }
    *psmtp=smtp;

GO_OUT:
    smtp_quit(smtp);
    return(rtn);
}/*}}}*/

void smtp_destroy(SMTP smtp)
{/*{{{*/
    if(smtp)
    {
        skt_close(smtp->skt);
        free(smtp->_readbuf);
        free(smtp->_writebuf);
        free(smtp);
    }
}/*}}}*/

int smtp_hello(SMTP smtp)
{/*{{{*/
    int res;
    char addr[FILE_NM_SIZE+1];
    int rtn=0;
    int timeout;
    char *readbuf, *writebuf;
    int offset;

    if(!smtp)
        return(RET_ERR_PARA);
    readbuf=smtp->_readbuf;
    writebuf=smtp->_writebuf;

    timeout=_TIMEOUT;
    sprintf(addr, "%s:%d", smtp->addrinfo.host, smtp->addrinfo.port);
    if(skt_connect(addr, 0, &timeout, &smtp->skt))  
    {   return(RET_ERR_CONNECT);   }
    if((res=skt_read(smtp->skt, (unsigned char *)readbuf, &offset,
                    SKT_R_RAW, &timeout)) < 0)
    {
        if(res == RET_ERR_TIMEOUT || res == RET_ERR_ABORT)
            rtn=res;
        else
            rtn=-2;
        goto GO_OUT;
    }
    readbuf[_SMTP_CODE_SIZE]=ZERO;
    if(atoi(readbuf) != 220)
    {   rtn=-3; goto GO_OUT;   }
    if(smtp->auth == _NOAUTH_)
    {
        offset=sprintf(writebuf, "HELO %s"CRLF, smtp->sender);
        if((res=_talk(smtp, offset)) < 0)
        {   rtn=res; goto GO_OUT;   }
        if(res != 250)
        {   rtn=-4; goto GO_OUT;   }
    }
    else
    {
        offset=sprintf(writebuf, "EHLO %s"CRLF, smtp->sender);
        if((res=_talk(smtp, offset)) < 0)
        {   rtn=res; goto GO_OUT;   }
        if(res != 250)
        {   rtn=-5; goto GO_OUT;   }
        if(smtp->auth == _LOGIN_)
        {
            offset=sprintf(writebuf, "AUTH LOGIN"CRLF);
            if((res=_talk(smtp, offset)) < 0)
            {   rtn=res; goto GO_OUT;   }
            if(res != 334)
            {   rtn=-6; goto GO_OUT;   }
            ossl_encode(OSSL_ENCODE_BASE64,
                    (unsigned char *)smtp->addrinfo.user,
                    strlen(smtp->addrinfo.user), readbuf, &offset);
            readbuf[offset-1]=ZERO;
            offset=sprintf(writebuf, "%s"CRLF, readbuf);
            if((res=_talk(smtp, offset)) < 0)
            {   rtn=res; goto GO_OUT;   }
            if(res != 334)
            {   rtn=-7; goto GO_OUT;   }
            ossl_encode(OSSL_ENCODE_BASE64,
                    (unsigned char *)smtp->addrinfo.passwd,
                    strlen(smtp->addrinfo.passwd), readbuf, &offset);
            readbuf[offset-1]=ZERO;
            offset=sprintf(writebuf, "%s"CRLF, readbuf);
            if((res=_talk(smtp, offset)) < 0)
            {   rtn=res; goto GO_OUT;   }
            if(res != 235)
            {   rtn=-8; goto GO_OUT;   }
        }
        else if(smtp->auth == _PLAIN_)
        {
            int size;
            char buf[100];

            strcpy(buf, smtp->addrinfo.user);
            size=strlen(smtp->addrinfo.user)+1;
            strcpy(buf+size, smtp->addrinfo.user);
            size+=strlen(smtp->addrinfo.user)+1;
            strcpy(buf+size, smtp->addrinfo.passwd);
            size+=strlen(smtp->addrinfo.passwd);
            ossl_encode(OSSL_ENCODE_BASE64, (unsigned char *)buf, size,
                    readbuf, &offset);
            readbuf[offset-1]=ZERO;
            offset=sprintf(writebuf, "AUTH PLAIN %s"CRLF, readbuf);
            if((res=_talk(smtp, offset)) < 0)
            {   rtn=res; goto GO_OUT;   }
            if(res != 235)
            {   rtn=-9; goto GO_OUT;   }
        }
    }

    return(0);

GO_OUT:
     skt_close(smtp->skt);
     smtp->skt=(SKT)NULL;
    return(rtn);
}/*}}}*/

struct _attach
{/*{{{*/
    char name[FILE_NM_SIZE+1];
    char *content;
    int size;
};/*}}}*/

static char *_get_attach_ext_desc(char *file)
{/*{{{*/
    static struct
    {
        char *ext;
        char *desc;
    } a_ext_desc[]=
    {
        { "xls", "application/excel" },
        { "hqx", "application/mac-binhex40" },
        { "doc", "application/msword" },
        { "bin", "application/octet-stream" },   
        { "dll", "application/octet-stream" },
        { "exe", "application/octet-stream" },
        { "pdf", "application/pdf" },
        { "p10", "application/pkcs10" },   
        { "p7c", "application/pkcs7-mime" },
        { "p7s", "application/pkcs7-signature" },   
        { "ai",  "application/postscript" },
        { "eps", "application/postscript" },   
        { "ps",  "application/postscript" },
        { "rtf", "application/rtf" },   
        { "fdf", "application/vnd.fdf" },
        { "arj", "application/x-arj" },   
        { "gz",  "application/x-gzip" },
        { "class","application/x-java-class" },   
        { "js",  "application/x-javascript" },
        { "lzh", "application/x-lzh" },   
        { "xml", "application/xml" },
        { "lnk", "application/x-ms-shortcut" },   
        { "p12", "application/x-pkcs12" },
        { "p7b", "application/x-pkcs7-certificates" },   
        { "swf", "application/x-shockwave-flash" },
        { "swfl","application/x-shockwave-flash" },   
        { "tar", "application/x-tar" },
        { "hlp", "application/x-winhelp" },   
        { "cer", "application/x-x509-ca-cert" },
        { "cert","application/x-x509-ca-cert" },   
        { "zip", "application/x-zip-compressed" },
        { "aif", "audio/aiff" },   
        { "aifc","audio/aiff" },
        { "aiff","audio/aiff" },   
        { "au",  "audio/basic" },
        { "snd", "audio/basic" },   
        { "mid", "audio/midi" },
        { "rmi", "audio/midi" },   
        { "mp3", "audio/mpeg" },
        { "vox", "audio/voxware" },   
        { "wav", "audio/wav" },
        { "ra",  "audio/x-pn-realaudio" },   
        { "ram", "audio/x-pn-realaudio" },
        { "bmp", "image/bmp" },
        { "gif", "image/gif" },
        { "jpeg","image/jpeg" },   
        { "jpg", "image/jpeg" },
        { "tif", "image/tiff" },   
        { "tiff","image/tiff" },
        { "xbm", "image/xbm" },   
        { "vrml","model/vrml" },
        { "wrl", "model/vrml" },   
        { "htm", "text/html" },
        { "html","text/html" },
        { "c",   "text/plain" },
        { "cpp", "text/plain" },   
        { "def", "text/plain" },
        { "h",   "text/plain" },   
        { "txt", "text/plain" },
        { "rtx", "text/richtext" },   
        { "java","text/x-java-source" },
        { "mpeg","video/mpeg" },   
        { "mpg", "video/mpeg" },
        { "avi", "video/msvideo" },   
        { "mov", "video/quicktime" },
        { "qt",  "video/quicktime" },   
        { "shtml","wwwserver/html-ssi" },
        { "asa", "wwwserver/isapi" },   
        { "asp", "wwwserver/isapi" },
        { "cfm", "wwwserver/isapi" },   
        { "dbm", "wwwserver/isapi" },
        { "isa", "wwwserver/isapi" },   
        { "plx", "wwwserver/isapi" },
        { "url", "wwwserver/redirection" },   
        { "cgi", "wwwserver/stdcgi" },
        { "php", "wwwserver/stdcgi" },   
        { "pl",  "wwwserver/stdcgi" },
        { "wcgi","wwwserver/wincgi" },   
        { "rar", "application/octet-stream" },
        { NULL, "application/x-unknown" }
    };
    char *p;
    int i;

    if((p=strrchr(file, '.')) == NULL || p[0] == ZERO)
        return("application/x-unknown");
    p++;
    for(i=0; a_ext_desc[i].ext; i++)
        if(strcasecmp(p, a_ext_desc[i].ext) == 0)
            break;
    return(a_ext_desc[i].desc);
}/*}}}*/

static void _generate_boundary(char *boundary)
{/*{{{*/
    static char *digits="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    unsigned int seed=0;
    int len=strlen(digits);
    int i;

    for(i=0; i<_BOUNDARY_SIZE; i++)
        boundary[i]=digits[random_int(0, len-1, &seed)];
    boundary[_BOUNDARY_SIZE]=ZERO;
}/*}}}*/

static void _free_attach(struct _attach *a_attach)
{/*{{{*/
    if(a_attach)
    {
        int i;
        struct _attach *patt;

        for(i=0; (patt=&a_attach[i])->content; i++)
            free(patt->content);
        free(a_attach);
    }
}/*}}}*/

static int _parse_person(char *person, char *name, char *email)
{/*{{{*/
    char *begin=strstr(person, "<");
    if(begin)
    {
        strncpy(name, person, begin-person);
        name[begin-person]=ZERO;
        begin+=strlen("<");
        char *end=strstr(begin, ">");
        if(!end) return(-1);
        strncpy(email, begin, end-begin);
        email[end-begin]=ZERO;
//        printf("%s %s\n", mailfrom_name, mailfrom_email);
    }
    else
    {
        CLR_BUF(name);
        strcpy(email, person);
    }
    return (0);
}/*}}}*/

int smtp_send(SMTP smtp, char *mailto,
        char *mailfrom, char *replyto,
        char *subject, char *body, char *attach[])
{/*{{{*/
    int res;
    int rtn=0;
    int timeout;
    int i;
    char **a_to=NULL;
    int to_num=0;
    char *writebuf=(char *)NULL;
    int offset;
    char body_content_type[50];
    struct _attach *a_attach=NULL;
    char boundary[_BOUNDARY_SIZE+1];

    if(!smtp || !mailto || !mailfrom)
        return(RET_ERR_PARA);

    // 解析mailfrom
    char mailfrom_name[LONG_NAME_SIZE+1];
    char mailfrom_email[LONG_NAME_SIZE+1];
    if(_parse_person(mailfrom, mailfrom_name, mailfrom_email))
        return(RET_ERR_PARA);
    char replyto_name[LONG_NAME_SIZE+1];
    char replyto_email[LONG_NAME_SIZE+1];
    if(replyto)
    {
        if(_parse_person(replyto, replyto_name, replyto_email))
            return(RET_ERR_PARA);
    }
    else
    {
        strcpy(replyto_name, mailfrom_name);
        strcpy(replyto_email, mailfrom_email);
    }

    // 解析发送邮件地址
    to_num=str_split(mailto, ';', &a_to);
    if(to_num <= 0)
        return(-2);
    // body描述
    //if(body && strcasestr(body, "<HTML>"))
    //    strcpy(body_content_type, "text/html; ");
    //else
    //    strcpy(body_content_type, "text/plain; ");
    //strcat(body_content_type, "charset=\"UTF-8\"");
    strcpy(body_content_type, "text/html;charset=utf-8");
    // 检查附件文件
    if(attach && attach[0])
    {
        struct _attach *patt;
        int total_size;
        int enc_size;

        ALLOC_MULTI_RETERR(a_attach, struct _attach,
                MAIL_MAX_ATTACH_NUM, RET_ERR_ALLOC);
        for(i=0; i<MAIL_MAX_ATTACH_NUM; i++)
        {
            patt=&a_attach[i];
            CLR_BUF(patt->name);
            patt->content=(char *)NULL;
            patt->size=0;
        }
        _generate_boundary(boundary);
        for(total_size=0, i=0; attach[i]; i++)
        {
            char *file=attach[i];
            struct stat sb;

            patt=&a_attach[i];
            extract_basename(file, patt->name);
            if(stat(file, &sb))
            {   rtn=-3; goto GO_OUT;   }
            if(sb.st_size > MAIL_MAX_ATTACH_SIZE)
            {   rtn=-4; goto GO_OUT;   }
            total_size+=sb.st_size;
            ALLOC_MULTI_DOERR(patt->content, char, sb.st_size*2+100,
                    rtn=RET_ERR_ALLOC; goto GO_OUT);
            offset=sprintf(patt->content,
                    "--%s"CRLF
                    "Content-Type:%s;name=\"%s\""CRLF
                    "Content-Transfer-Encoding:base64"CRLF
                    CRLF,
                    boundary,
                    _get_attach_ext_desc(patt->name), patt->name);
            if(ossl_encode_file(OSSL_ENCODE_BASE64, file,
                        patt->content+offset, &enc_size))
            {   rtn=-5; goto GO_OUT;   }
            offset+=enc_size;
            offset+=sprintf(patt->content+offset, CRLF);
            patt->size=offset;
        }
        if(total_size > MAIL_MAX_ATTACH_TOTAL_SIZE)
        {   rtn=-6; goto GO_OUT;   }
    }

    writebuf=smtp->_writebuf;
    offset=sprintf(writebuf, "MAIL FROM:<%s>"CRLF, mailfrom_email);
    if((res=_talk(smtp, offset)) < 0)
    {   rtn=res; goto GO_OUT;   }
    if(res != 250)
    {   rtn=-7; goto GO_OUT;   }

    i=strlen(body);
    if(i+KB(1) > smtp->_writebuf_size)
    {
        EXPAND_ARRAY_DOERR(smtp->_writebuf, char,
                pack_size(i+KB(1)-smtp->_writebuf_size, KB(4)),
                smtp->_writebuf_size,
                rtn=RET_ERR_ALLOC; goto GO_OUT);
        writebuf=smtp->_writebuf;
    }
    for(i=0; i<to_num; i++)
    {
        char *to=a_to[i];
        offset=sprintf(writebuf, "RCPT TO:<%s>"CRLF, to);
        if((res=_talk(smtp, offset)) < 0)
        {   rtn=res; goto GO_OUT;   }
        if(res != 250)
        {   rtn=-8; goto GO_OUT;   }
    }

    offset=sprintf(writebuf, "DATA"CRLF);
    if((res=_talk(smtp, offset)) < 0)
    {   rtn=res; goto GO_OUT;   }
    if(res != 354)
    {   rtn=-9; goto GO_OUT;   }
    offset=sprintf(writebuf,
            "To:<%s>"CRLF
            "From:%s<%s>"CRLF
            "Reply-To:%s<%s>"CRLF
            "Subject:%s"CRLF"MIME-Version:1.0"CRLF,
            mailto,
            mailfrom_name, mailfrom_email,
            replyto_name, replyto_email,
            subject);
    if(a_attach)
    {
        struct _attach *patt;

        offset+=sprintf(writebuf+offset,
                "Content-Type:multipart/mixed;boundary=\"%s\""CRLF
                CRLF
                "This is a multi-part message in MIME format."CRLF
                "--%s"CRLF
                "Content-Type:%s"CRLF
                "Content-Transfer-Encoding:8bit"CRLF
                CRLF
                "%s"CRLF
                CRLF,
                boundary,
                boundary,
                body_content_type,
                body);
        timeout=_TIMEOUT;
        //printf(writebuf);
        if(skt_write(smtp->skt, (unsigned char *)writebuf, &offset,
                    SKT_W_SIZE, &timeout) < 0)
        {   rtn=-10; goto GO_OUT;   }
        for(i=0; (patt=&a_attach[i])->content; i++)
        {
            offset=patt->size;
            timeout=_TIMEOUT*5;
            //printf(patt->content);
            if(skt_write(smtp->skt, (unsigned char *)patt->content,
                        &offset, SKT_W_SIZE, &timeout) < 0)
            {   rtn=-11; goto GO_OUT;   }
        }
        offset=sprintf(writebuf, "--%s--"CRLF, boundary);
    }
    else
    {
        offset+=sprintf(writebuf+offset,
                "Content-Type:%s"CRLF
                "Content-Transfer-Encoding:8bit"CRLF
                CRLF
                "%s"CRLF,
                body_content_type, body);
    }

    offset+=sprintf(writebuf+offset, CRLF"."CRLF);
    if((res=_talk(smtp, offset)) < 0)
    {   rtn=res; goto GO_OUT;   }
    if(res != 250)
    {   rtn=-12; goto GO_OUT;   }

GO_OUT:
    _free_attach(a_attach);
    if(rtn > RET_ERR_GENERAL_START)
    {
        offset=sprintf(writebuf, "RSET"CRLF);
        if((res=_talk(smtp, offset)) < 0)
            rtn=res;
        else if(res != 250 && rtn == 0)
            rtn=-13;
    }
    FREE_PTR_ARRAY(a_to, to_num);
    return(rtn);
}/*}}}*/

int smtp_quit(SMTP smtp)
{/*{{{*/
    int rtn=0;

    if(!smtp)
        return(RET_ERR_PARA);

    if(smtp->skt)
    {
        int offset;
        int res;

        offset=sprintf(smtp->_writebuf, "QUIT"CRLF);
        if((res=_talk(smtp, offset)) < 0)
            rtn=res;
        else if(res != 221)
            rtn=-2;
        skt_close(smtp->skt);
        smtp->skt=(SKT)NULL;
    }
    return(rtn);
}/*}}}*/

int smtp_quick_send(SMTP smtp, char *mailto,
        char *mailfrom, char *replyto,
        char *subject, char *body, char *attach[])
{/*{{{*/
    int rtn;

    if((rtn=smtp_hello(smtp)) == 0)
    {
        if((rtn=smtp_send(smtp, mailto, mailfrom, replyto,
                        subject, body, attach)) == 0)
        {}
        else
        {
            if(rtn > RET_ERR_GENERAL_START)
                rtn+=-100;
        }
        smtp_quit(smtp);
    }

    return(rtn);
}/*}}}*/
